#include "menudialog.h"
#include "ui_menudialog.h"

#include <QAbstractTableModel>
#include <QSettings>
#include <QTimer>
#include <QKeyEvent>
#include <QDebug>

class MenuModel: public QAbstractTableModel, public QList<QAction*>
{
public: // from QAbstractTableModel
    int columnCount(const QModelIndex& /*parent*/) const;
    int rowCount(const QModelIndex& /*parent*/) const;
    Qt::ItemFlags flags(const QModelIndex& index) const;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
    bool setData(const QModelIndex& index, const QVariant& value, int role);
};

int MenuModel::columnCount(const QModelIndex& /*parent*/) const
{
    return 2;
}

int MenuModel::rowCount(const QModelIndex& /*parent*/) const
{
    return count();
}

Qt::ItemFlags MenuModel::flags(const QModelIndex& index) const
{
    // default is selectable and enabled
    Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    // setting values are also editable
    const QAction* action = at(index.row());
    if (action && !action->whatsThis().isEmpty() && index.column() == 1) {
        flags |= Qt::ItemIsEditable;
    }
    return flags;
}

QVariant MenuModel::data(const QModelIndex& index, int role) const
{
    const QAction* action = at(index.row());
    Q_ASSERT(action);

    // global roles
    if (role == Qt::TextAlignmentRole) {
        return index.column() == 0? Qt::AlignRight : Qt::AlignLeft;
    }

    // action-specific roles
    if (action->whatsThis().isEmpty()) { // action item
        if (role == Qt::DisplayRole) {
            if (index.column() == 0) {
                return ">>"; // TODO: Action icon
            }
            else {
                return action->text();
            }
        }
        else if (role == Qt::FontRole) {
            QFont f;
            f.setBold(true);
            return f;
        }
    }
    else if (!action->whatsThis().isEmpty()) { // settings item
        QSettings settings;
        if (role == Qt::DisplayRole) {
            if (index.column() == 0) {
                return action->text();
            }
            else {
                return settings.value(action->whatsThis()); // return setting value
            }
        }
        else if (role == Qt::EditRole) {
            if (index.column() == 1) {
//                if (!action->data().isNull())
//                    return action->data().value<const QMap<QString,QVariant>&>().keys();
                return settings.value(action->whatsThis()); // return setting value
            }
        }
        else if (role == Qt::FontRole) {
            if (index.column() == 0) {
                QFont f;
                f.setBold(true);
                return f;
            }
        }
    }

    // use default
    return QVariant();
}

bool MenuModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    QAction* action = at(index.row());
    if (!action || role != Qt::EditRole)
        return false;

    QSettings settings;
    QString key = action->whatsThis();
    if (key.isEmpty() || !settings.contains(key))
        return false;

    settings.setValue(key, value);
    emit dataChanged(index, index);
    return true;
}

// -------------------------------------------------------------------------------------

MenuDialog::MenuDialog(QWidget *parent):
        QDialog(parent, Qt::SplashScreen),
        ui(new Ui::MenuDialog),
        model(new MenuModel)
{
    ui->setupUi(this);

    ui->actionTable->setFocus();
    activateWindow();

    // resize window just after it has been executed
    QTimer::singleShot(0, this, SLOT(setup()));
}

MenuDialog::~MenuDialog()
{
    delete ui;
    delete model;
}

void MenuDialog::addActionItem(QAction* action)
{
    // this item will not be owned by model
    model->append(action);
    connect(action, SIGNAL(triggered()), SLOT(accept()));
}

void MenuDialog::addActionItem(const QString& label, QObject* receiver, const char* method)
{
    QAction* action = new QAction(label, model);
    if (action) {
        connect(action, SIGNAL(triggered()), SLOT(accept()));
        receiver->connect(action, SIGNAL(triggered()), method);
        model->append(action);
    }
}

void MenuDialog::addSettingsItem(const QString& label, const QString& settingKey)
{
    QAction* action = new QAction(label, model);
    if (action) {
        action->setWhatsThis(settingKey);
        model->append(action);
    }
}

//void MenuDialog::addSettingsItem(const QString& label, const QString& settingKey,
//                                 const QMap<QString,QVariant>& items)
//{
//    QAction* action = new QAction(label, model);
//    if (action) {
//        action->setWhatsThis(settingKey);
//        action->setData(items);
//        model->append(action);
//    }
//}

void MenuDialog::changeEvent(QEvent *e)
{
    QDialog::changeEvent(e);
    switch (e->type()) {
        case QEvent::LanguageChange:
            ui->retranslateUi(this);
            break;
        default:
            break;
    }
}

void MenuDialog::keyPressEvent(QKeyEvent* event)
{
    switch (event->key()) {
        case Qt::Key_Enter:
        case Qt::Key_Return:
        case Qt::Key_Play:
        case Qt::Key_MediaPlay:
            // trigger actions on terminal items (editables handled by table?)
            triggerAction(ui->actionTable->currentIndex());
            return;
    }

    QDialog::keyPressEvent(event);
}

void MenuDialog::setup()
{
    ui->actionTable->setModel(model);
    setModal(true);

    // make row selection to trigger the respective action
    connect(ui->actionTable, SIGNAL(activated(const QModelIndex&)),
            SLOT(triggerAction(const QModelIndex&)));

    // resize and focus to first row
    ui->actionTable->resizeColumnsToContents();
    ui->actionTable->resizeRowsToContents();
    
    // find the optimal size for the dialog
    QModelIndex dummy;
    int w = 0, h = 0, lm = 0, tm = 0, rm = 0, bm = 0;
    for (int i = 0; i < model->columnCount(dummy); ++i)
        w += ui->actionTable->columnWidth(i);
    for (int i = 0; i < model->rowCount(dummy); ++i)
        h += ui->actionTable->rowHeight(i);
    ui->actionTableLayout->getContentsMargins(&lm, &tm, &rm, &bm);

    // limit the dialog size to maximum coverage
    QSettings settings;
    double maxCover = settings.value("ui/maxDialogCover").toDouble();
    QSize newSize(qMin(int(parentWidget()->width() * maxCover), w+lm+rm),
                  qMin(int(parentWidget()->height() * maxCover), h+tm+bm));

    // set the new size and move the dialog in the middle of parent
    QRect g = geometry();
    g.setSize(newSize);
    setGeometry(g.translated(parentWidget()->geometry().center() - g.center()));
    
    // select first row if there is one
    if (model->count() > 0)
        ui->actionTable->selectRow(0);
}

void MenuDialog::triggerAction(const QModelIndex& index)
{
    QAction* action = model->at(index.row());
    if (action) {
        if (action->whatsThis().isEmpty()) {
            action->trigger(); // trigger the action item's action
        }
        else if (!action->whatsThis().isEmpty()) {
            // start editing column 1 (the only editable column)
            ui->actionTable->edit(model->index(index.row(), 1, index));
        }
    }
}
